#! /usr/local/bin/python
#-*- coding: utf-8 -*-


"""
pyChatter. A multi-agent based instant messaging written in Python.
Copyright (C) 2010  Cedric Bonhomme

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

pyChatter  Copyright (C) 2010  Cedric Bonhomme
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
"""

__author__ = "Cedric Bonhomme"
__version__ = "$Revision: 0.1 $"
__date__ = "$Date: 2010/01/04 $"
__copyright__ = "Copyright (c) 2010 Cedric Bonhomme"
__license__ = "GPLv3"

import os
import sys

sys.path.append('./spade/')

import spade

import xmlParser

class AuthenticationAgent(spade.Agent.Agent):
    """
    This class represent the authentication agent.
    This agent is in charge of serving requests of
    client agent.
    Responsibilities:
     - manage the XML file of known client agents;
     - provides informations (ontology, public key) to a client about an other client.
    """

    class ReceiveMessage(spade.Behaviour.Behaviour):
        """
        This behaviour is in charge of receiving incoming messages
        from clients.
        """
        def onStart(self):
            """
            """
            print "Starting", self.__class__.__name__, "behaviour"

            self.myAgent.register()

        def _process(self):
            """
            Process the request of client.
            """
            self.msg = None

            # Blocking receive indefinitely
            self.msg = self._receive(True)

            # Check wether the message arrived (incoming request from a client)
            if self.msg:
                self.myAgent.outgoing_message = None
                command = self.msg.getContent().split(':')

                # REGISTRATION
                if command[0] == "signin":
                    print "Incomming signin request from", self.msg.getSender().getName()
                    xml = xmlParser.add_client(command[1], command[2], \
                                        command[3], command[4])
                    if xml == 'already_registered':
                        self.myAgent.outgoing_message = xml
                        print "Request of", self.msg.getSender().getName(), "rejected:"
                        print xml
                    else:
                        xmlParser.write(xml)
                        print "Request of", self.msg.getSender().getName(), "accepted."

                elif command[0] == "signout":
                    pass

                # AUTHENTICATION
                elif command[0] == "login":
                    print "Incomming authentication request from", self.msg.getSender().getName()
                    if command[2] == xmlParser.get_password(command[1]):
                        ontology = xmlParser.get_ontology(command[1])
                        # Provides the ontology to the client for the bevaviour
                        # in charge of receiving incoming messages from other clients.
                        self.myAgent.outgoing_message = "authentication_success:" + \
                                                            ontology
                        print "Request of", self.msg.getSender().getName(), "accepted."
                    else:
                        self.myAgent.outgoing_message = "authentication_failed"
                        print "Request of", self.msg.getSender().getName(), "rejected."

                # ADDITION OF INTERLOCUTOR
                elif command[0] == "addinterlocutor":
                    print self.msg.getSender().getName(), "asked informations about", command[1]
                    pubkey = str(xmlParser.get_pub_key(command[1]))
                    ontology = str(xmlParser.get_ontology(command[1]))

                    # Provides to the client (Bob) the ontology and RSA public key
                    # of the new interlocutor (Alice).
                    self.myAgent.outgoing_message = "newinterlocutor:" + command[1] + \
                                            ":" + ontology + ":" + pubkey

                print ""
                # SEND the reply to the client.
                if self.myAgent.outgoing_message != None:
                    # Prepare the receiver AID
                    self.myAgent.receiver = self.msg.getSender()

                    # Send the message with a OneShotBehaviour
                    self.myAgent.addBehaviour(self.myAgent.SendMessage(), None)




    class SendMessage(spade.Behaviour.OneShotBehaviour):
        """
        This behaviour is in charge of sending messages (answers)
        to the client.
        """
        def onStart(self):
            print "Starting", self.__class__.__name__, "behaviour"

        def _process(self):
            """
            Send a message.
            """
            # First, form the receiver AID
            receiver = self.myAgent.receiver

            # Second, build the message
            self.msg = spade.ACLMessage.ACLMessage()          # Instantiate the message
            self.msg.setPerformative("inform")                # Set the "inform" FIPA performative
            self.msg.setOntology("authentication")            # Set the ontology of the message content
            self.msg.setLanguage("English")                   # Set the language of the message content
            self.msg.addReceiver(receiver)                    # Add the message receiver
            self.msg.setContent(self.myAgent.outgoing_message)# Set the message content

            # Third, send the message with the "send" method of the agent
            self.myAgent.send(self.msg)

        def onEnd(self):
            print "Ending", self.__class__.__name__, "behaviour"


    def _setup(self):
        """
        Initializes the agent.
        """
        print "Starting", self.getName(), "agent"

        self.receiver = None
        self.outgoing_message = None

        cooking_template = spade.Behaviour.ACLTemplate()
        cooking_template.setOntology("authentication")
        mt = spade.Behaviour.MessageTemplate(cooking_template)
        # Add the behaviour WITH the template
        receive_messabe_behaviour = self.ReceiveMessage()
        self.addBehaviour(receive_messabe_behaviour, mt)

    def tearDown(self):
        """
        """
        self.deregister()
        self.stop()

    def register(self):
        """
        Register the services provided by the agent.
        """
        dad = spade.DF.DfAgentDescription()

        sd = spade.DF.ServiceDescription()
        sd.setName(self.getName())
        sd.setType("authentication_agent")
        dad.addService(sd)

        dad.setAID(self.getAID())

        try:
            result = self.registerService(dad)
        except Exception, e:
            print "Registration of", self.getName(), "failed"

    def deregister(self):
        """
        Unregister the agent from the Directory Facilitator.
        """
        dad = spade.DF.DfAgentDescription()
        dad.setAID(self.getAID())
        try:
            self.deregisterService(dad)
        except Exception, e:
            print "Deregistration of", self.getName(), "failed"

    def search_agent_into_df(self, service_name = None, service_type = None):
        """
        Search for a client agent according to its service name.
        """
        dad = spade.DF.DfAgentDescription()
        ds = spade.DF.ServiceDescription()
        if service_type == None:
            ds.setType("client_agent")
            ds.setName(service_name)
        else:
            ds.setType(service_type)
        dad.addService(ds)

        return self.searchService(dad)


if __name__ == "__main__":
    # Point of entry in execution mode
    from optparse import OptionParser
    parser = OptionParser()
    parser.add_option("-n", "--agent-name", dest="agent_name",
                    help="Name of the authentication agent")
    parser.add_option("-s", "--agent-host", dest="agent_host",
                    help="Address of the SPADE platform")
    parser.set_defaults(agent_name = 'authenticator',
                    agent_host = '127.0.0.1')

    (options, args) = parser.parse_args()


    authentication_agent = AuthenticationAgent(options.agent_name + "@" + options.agent_host, \
                                "secret")
    authentication_agent.start()